package locsi;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.TitledBorder;

import model.editormodel.EditorModel;
import model.editormodel.LocsiParser;
import model.simulationmodel.SimulationModel;
import view.UtilityConstants;
import view.commonview.LocsiFrame;
import controller.commoncontroller.UserManualListener;

/**
 * <b>Hauptklasse:</b> Diese Klasse beinhaltet die MainMethode <br>
 * <b>In Verbindung stehende Klassen:</b> <code>LocsiFrame, EditorModel,
 * SimulationModel</code><br>
 * <br>
 * Startklasse mit Main-Methode. Der Schaltungssimulator Locsi
 * (Logical-Circuit-Simulator) dient zur Erstellung, graphischen Darstellung in
 * 2D, Verifizierung und Simulation von Schaltnetzen und Schaltwerken. <br>
 * <br>
 * In dieser Klasse werden die folgenden Objekte erzeugt: LocsiFrame,
 * EditorModel, SimulationModel.
 * 
 * @author Team Locsi
 * 
 */
public class Locsi {

	/**
	 * Initialisierung von Locsi
	 * 
	 * @param args
	 *            wird nicht benoetigt
	 */
	public static void main(String[] args) throws InterruptedException {
		UserSettings.loadingWasSuccessful = UserSettings.loadSettingsFile();
		loadResources();
		final EditorModel eModel = new EditorModel();
		final SimulationModel sModel = new SimulationModel(eModel);
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI(eModel, sModel);
			}
		});
	}

	/**
	 * In dieser Methode werden die benoetigten Ressourcen aus dem jar-File
	 * extrahiert und als Datei auf dem Dateisystem des Benutzers gespeichert.
	 * (Benutzerhandbuch, XSD-Schema, Full-Adder, Carry-Ripple)
	 */
	private static void loadResources() {
		ClassLoader loader = new ClassLoader() {
		};
		
		//streams für xsd-schema und benutzerhandbuch
		InputStream manualStream = loader
				.getResourceAsStream((UtilityConstants.LOCSI_DATA + "manual.pdf"));
		InputStream xsdStream = loader
				.getResourceAsStream(LocsiParser.XSD_SCHEME);

		// Streams für die selbstdefinierten Bauteile + dessen Bilder
		InputStream fullAdderXml = loader
				.getResourceAsStream(UtilityConstants.LOCSI_DATA
						+ "selfdefined_gates/full_adder.xml");
		InputStream fullAdderPng = loader
				.getResourceAsStream(UtilityConstants.LOCSI_DATA
						+ "selfdefined_gates/full_adder.png");
		InputStream carryRippleXml = loader
				.getResourceAsStream(UtilityConstants.LOCSI_DATA
						+ "selfdefined_gates/carry_ripple_4bit.xml");
		InputStream carryRipplePng = loader
				.getResourceAsStream(UtilityConstants.LOCSI_DATA
						+ "selfdefined_gates/carry_ripple_4bit.png");

		int bytesReadYet = 0;
		JProgressBar bar;

		try {
			
			//Splashscreen bzw. Ladeanzeige initialisieren
			ImageIcon logoIcon = new ImageIcon(
					loader.getResource(UtilityConstants.ICON_PATH + "logo.png"));
			logoIcon.setImage(logoIcon.getImage().getScaledInstance(240, 120,
					Image.SCALE_DEFAULT));
			JLabel logoLabel = new JLabel(logoIcon);
			logoLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
			int bytesToRead = manualStream.available() + xsdStream.available()
					+ fullAdderXml.available() + fullAdderPng.available()
					+ carryRippleXml.available() + carryRipplePng.available();
			bar = new JProgressBar(0, bytesToRead);
			bar.setBackground(Color.WHITE);
			bar.setForeground(Color.ORANGE);
			bar.setFont(new Font(bar.getFont().getName(), bar.getFont()
					.getStyle(), 20));
			logoLabel.setOpaque(false);
			logoLabel.setBackground(Color.WHITE);
			JDialog dialog = new JDialog();
			dialog.setBackground(Color.WHITE);
			dialog.getContentPane().setBackground(Color.white);
			dialog.setSize(500, 300);
			dialog.add(logoLabel, BorderLayout.CENTER);
			dialog.add(bar, BorderLayout.SOUTH);
			dialog.setLocationRelativeTo(null);
			dialog.setUndecorated(true);
			((JPanel) (dialog.getContentPane())).setBorder(BorderFactory
					.createRaisedBevelBorder());
			dialog.setVisible(true);

			// Benutzerhandbuch
			bar.setBorderPainted(false);
			logoLabel.setBorder(BorderFactory.createTitledBorder(
					BorderFactory.createLineBorder(Color.WHITE),
					"Lade Ressourcen...", TitledBorder.DEFAULT_JUSTIFICATION,
					TitledBorder.BOTTOM));
			
			//variante fuer temporäres file (jedes mal neu erzeugen)
			UserManualListener.manual = File.createTempFile("locsi_manual", ".pdf");
			UserManualListener.manual.deleteOnExit();
			extractFileFromJar(manualStream, UserManualListener.manual, bar, bytesReadYet);
			
			//variante fuer file das gespeichert wird und nur erzeugt wird
			//wenn es nicht vorhanden ist
//			UserManualListener.manual = new File("manual.pdf");
//			if (UserManualListener.manual.createNewFile()) {
//				extractFileFromJar(manualStream, UserManualListener.manual, bar, bytesReadYet);
//			}
			
			// XSD-Schema
			logoLabel.setBorder(BorderFactory.createTitledBorder(
					BorderFactory.createLineBorder(Color.WHITE),
					"Lade XSD-Schema...", TitledBorder.DEFAULT_JUSTIFICATION,
					TitledBorder.BOTTOM));
			LocsiParser.schemaFile = File
					.createTempFile("circuits-1.0", ".xsd");
			LocsiParser.schemaFile.deleteOnExit();
			extractFileFromJar(xsdStream, LocsiParser.schemaFile, bar, bytesReadYet);

			// Selbstdefinierte Bauteile (mitgeliefert: full_adder, carry_ripple)
			logoLabel.setBorder(BorderFactory.createTitledBorder(
					BorderFactory.createLineBorder(Color.WHITE),
					"Lade Bauteile...", TitledBorder.DEFAULT_JUSTIFICATION,
					TitledBorder.BOTTOM));
			File selfDefDir = new File(UtilityConstants.SD_DIRECTORY);

			// die mitgelieferten bauteile werden nur initialisiert wenn der
			// selfdefined-ordner noch nicht existiert hat
			if (!selfDefDir.exists()) {
				selfDefDir.mkdir();

				File fullAdderXmlFile = new File(UtilityConstants.SD_DIRECTORY
						+ "full_adder.xml");
				File fullAdderPngFile = new File(UtilityConstants.SD_DIRECTORY + "full_adder.png");
				File carryRippleXmlFile = new File(UtilityConstants.SD_DIRECTORY + "carry_ripple_4bit.xml");
				File carryRipplePngFile = new File(UtilityConstants.SD_DIRECTORY + "carry_ripple_4bit.png");
				extractFileFromJar(fullAdderXml, fullAdderXmlFile, bar, bytesReadYet);
				extractFileFromJar(fullAdderPng, fullAdderPngFile, bar, bytesReadYet);
				extractFileFromJar(carryRippleXml, carryRippleXmlFile, bar, bytesReadYet);
				extractFileFromJar(carryRipplePng, carryRipplePngFile, bar, bytesReadYet);
			}

			dialog.dispose();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Extrahiert eine bestimmte Datei im JAR-Verzeichnis
	 * @param source Quelle von der extrahiert werden soll
	 * @param target Ziel-Datei zu der extrahiert werden soll
	 * @param bar JProgressBar die auskunft über vorranschreiten des Prozesses gibt
	 * @param bytesReadYet bisher gelesene bytes
	 * @throws IOException
	 */
	private static void extractFileFromJar(InputStream source, File target, JProgressBar bar, int bytesReadYet) throws IOException {
		BufferedInputStream reader = new BufferedInputStream(source);
		BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(target));
		int actRead;
		while ((actRead = reader.read()) != -1) {
			writer.write(actRead);
			bytesReadYet++;
			bar.setValue(bytesReadYet);
		}
		writer.close();
		reader.close();
	}

	/**
	 * Model und GUI werden erzeugt.
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 * @param sModel
	 *            <code>SimulationModel</code> - Referenz auf das
	 *            <code>SimulationModel</code>
	 */
	private static void createAndShowGUI(EditorModel eModel,
			SimulationModel sModel) {
		new LocsiFrame(eModel, sModel);
	}
}